/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
'use strict'

var activitiModeler = angular.module('activitiModeler', [
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'ngRoute',
  'ngDragDrop',
  'mgcrea.ngStrap',
  'ngGrid',
  'ngAnimate',
  'pascalprecht.translate',
  'duScroll'
])

var activitiModule = activitiModeler

activitiModeler
  // Initialize routes
  .config(['$selectProvider', '$translateProvider', function ($selectProvider, $translateProvider) {
    // Override caret for bs-select directive
    angular.extend($selectProvider.defaults, {
      caretHtml: '&nbsp;<i class="icon icon-caret-down"></i>'
    })

    // Initialize angular-translate
    $translateProvider.useStaticFilesLoader({
      prefix: './editor-app/i18n/',
      suffix: '.json'
    })

    $translateProvider.preferredLanguage('zh-CN')

    // remember language
    $translateProvider.useCookieStorage()
  }])
  .run(['$rootScope', '$timeout', '$modal', '$translate', '$location', '$window', '$http', '$q',
    function ($rootScope, $timeout, $modal, $translate, $location, $window, $http, $q) {
      $rootScope.config = ACTIVITI.CONFIG

      $rootScope.editorInitialized = false

      $rootScope.editorFactory = $q.defer()

      $rootScope.forceSelectionRefresh = false

      $rootScope.ignoreChanges = false // by default never ignore changes

      $rootScope.validationErrors = []

      $rootScope.staticIncludeVersion = Date.now()

      /**
       * A 'safer' apply that avoids concurrent updates (which $apply allows).
       */
      $rootScope.safeApply = function (fn) {
        var phase = this.$root.$$phase
        if (phase == '$apply' || phase == '$digest') {
          if (fn && (typeof (fn) === 'function')) {
            fn()
          }
        } else {
          this.$apply(fn)
        }
      }

      /**
       * Initialize the event bus: couple all Oryx events with a dispatch of the
       * event of the event bus. This way, it gets much easier to attach custom logic
       * to any event.
       */

      /* Helper method to fetch model from server (always needed) */
      function fetchModel (modelId) {
        var modelUrl = KISBPM.URL.getModel(modelId)
        var headers = {}
        var token = EDITOR.UTIL.getParameterByName('token')
        token = token && token.replace(/^"|"$/g, '')
        headers[$rootScope.config.token] = token
        $http({
          method: 'GET',
          url: modelUrl,
          headers: headers
        })
          .success(function ({ data }, status, headers, config) {
            $rootScope.editor = new ORYX.Editor(data)
            $rootScope.modelData = angular.fromJson(data)
            $rootScope.editorFactory.resolve()
          })
          .error(function (data, status, headers, config) {
            console.log('Error loading model with id ' + modelId + ' ' + data)
          })
      }

      function initScrollHandling () {
        var canvasSection = jQuery('#canvasSection')
        canvasSection.scroll(function () {
          // Hides the resizer and quick menu items during scrolling

          var selectedElements = $rootScope.editor.selection
          var subSelectionElements = $rootScope.editor._subSelection

          $rootScope.selectedElements = selectedElements
          $rootScope.subSelectionElements = subSelectionElements
          if (selectedElements && selectedElements.length > 0) {
            $rootScope.selectedElementBeforeScrolling = selectedElements[0]
          }

          jQuery('.Oryx_button').each(function (i, obj) {
            $rootScope.orginalOryxButtonStyle = obj.style.display
            obj.style.display = 'none'
          })

          jQuery('.resizer_southeast').each(function (i, obj) {
            $rootScope.orginalResizerSEStyle = obj.style.display
            obj.style.display = 'none'
          })
          jQuery('.resizer_northwest').each(function (i, obj) {
            $rootScope.orginalResizerNWStyle = obj.style.display
            obj.style.display = 'none'
          })
          $rootScope.editor.handleEvents({ type: ORYX.CONFIG.EVENT_CANVAS_SCROLL })
        })

        canvasSection.scrollStopped(function () {
          // Puts the quick menu items and resizer back when scroll is stopped.

          $rootScope.editor.setSelection([]) // needed cause it checks for element changes and does nothing if the elements are the same
          $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements)
          $rootScope.selectedElements = undefined
          $rootScope.subSelectionElements = undefined

          function handleDisplayProperty (obj) {
            if (jQuery(obj).position().top > 0) {
              obj.style.display = 'block'
            } else {
              obj.style.display = 'none'
            }
          }

          jQuery('.Oryx_button').each(function (i, obj) {
            handleDisplayProperty(obj)
          })

          jQuery('.resizer_southeast').each(function (i, obj) {
            handleDisplayProperty(obj)
          })
          jQuery('.resizer_northwest').each(function (i, obj) {
            handleDisplayProperty(obj)
          })
        })
      }

      /**
       * Initialize the Oryx Editor when the content has been loaded
       */
      $rootScope.$on('$includeContentLoaded', function (event) {
        if (!$rootScope.editorInitialized) {
          ORYX._loadPlugins()

          var modelId = EDITOR.UTIL.getParameterByName('modelId')
          fetchModel(modelId)

          $rootScope.window = {}
          var updateWindowSize = function () {
            $rootScope.window.width = $window.innerWidth
            $rootScope.window.height = $window.innerHeight
          }

          // Window resize hook
          angular.element($window).bind('resize', function () {
            $rootScope.safeApply(updateWindowSize())
          })

          $rootScope.$watch('window.forceRefresh', function (newValue) {
            if (newValue) {
              $timeout(function () {
                updateWindowSize()
                $rootScope.window.forceRefresh = false
              })
            }
          })

          updateWindowSize()

          // Hook in resizing of main panels when window resizes
          // TODO: perhaps move to a separate JS-file?
          jQuery(window).resize(function () {
            // Calculate the offset based on the bottom of the module header
            var offset = jQuery('#editor-header').offset()
            var propSectionHeight = jQuery('#propertySection').height()
            var canvas = jQuery('#canvasSection')

            if (offset == undefined || offset === null ||
              propSectionHeight === undefined || propSectionHeight === null ||
              canvas === undefined || canvas === null) {
              return
            }

            if ($rootScope.editor) {
              var selectedElements = $rootScope.editor.selection
              var subSelectionElements = $rootScope.editor._subSelection

              $rootScope.selectedElements = selectedElements
              $rootScope.subSelectionElements = subSelectionElements
              if (selectedElements && selectedElements.length > 0) {
                $rootScope.selectedElementBeforeScrolling = selectedElements[0]

                $rootScope.editor.setSelection([]) // needed cause it checks for element changes and does nothing if the elements are the same
                $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements)
                $rootScope.selectedElements = undefined
                $rootScope.subSelectionElements = undefined
              }
            }

            var totalAvailable = jQuery(window).height() - offset.top - 40 - 21
            canvas.height(totalAvailable - propSectionHeight)
            jQuery('#paletteSection').height(totalAvailable)

            // Update positions of the resize-markers, according to the canvas

            var actualCanvas = null
            if (canvas && canvas[0].children[1]) {
              actualCanvas = canvas[0].children[1]
            }

            var canvasTop = canvas.position().top
            var canvasLeft = canvas.position().left
            var canvasHeight = canvas[0].clientHeight
            var canvasWidth = canvas[0].clientWidth
            var iconCenterOffset = 8
            var widthDiff = 0

            var actualWidth = 0
            if (actualCanvas) {
              // In some browsers, the SVG-element clientwidth isn't available, so we revert to the parent
              actualWidth = actualCanvas.clientWidth || actualCanvas.parentNode.clientWidth
            }

            if (actualWidth < canvas[0].clientWidth) {
              widthDiff = actualWidth - canvas[0].clientWidth
              // In case the canvas is smaller than the actual viewport, the resizers should be moved
              canvasLeft -= widthDiff / 2
              canvasWidth += widthDiff
            }

            var iconWidth = 17
            var iconOffset = 20

            var north = jQuery('#canvas-grow-N')
            north.css('top', canvasTop + iconOffset + 'px')
            north.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px')

            var south = jQuery('#canvas-grow-S')
            south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px')
            south.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px')

            var east = jQuery('#canvas-grow-E')
            east.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px')
            east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px')

            var west = jQuery('#canvas-grow-W')
            west.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px')
            west.css('left', canvasLeft + iconOffset + 'px')

            north = jQuery('#canvas-shrink-N')
            north.css('top', canvasTop + iconOffset + 'px')
            north.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px')

            south = jQuery('#canvas-shrink-S')
            south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px')
            south.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px')

            east = jQuery('#canvas-shrink-E')
            east.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px')
            east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px')

            west = jQuery('#canvas-shrink-W')
            west.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px')
            west.css('left', canvasLeft + iconOffset + 'px')
          })

          jQuery(window).trigger('resize')

          jQuery.fn.scrollStopped = function (callback) {
            jQuery(this).scroll(function () {
              var self = this
              var $this = jQuery(self)
              if ($this.data('scrollTimeout')) {
                clearTimeout($this.data('scrollTimeout'))
              }
              $this.data('scrollTimeout', setTimeout(callback, 50, self))
            })
          }

          // Always needed, cause the DOM element on which the scroll event listeners are attached are changed for every new model
          initScrollHandling()

          $rootScope.editorInitialized = true
        }
      })

      /**
       * Initialize the event bus: couple all Oryx events with a dispatch of the
       * event of the event bus. This way, it gets much easier to attach custom logic
       * to any event.
       */

      $rootScope.editorFactory.promise.then(function () {
        KISBPM.eventBus.editor = $rootScope.editor

        var eventMappings = [
          { oryxType: ORYX.CONFIG.EVENT_SELECTION_CHANGED, kisBpmType: KISBPM.eventBus.EVENT_TYPE_SELECTION_CHANGE },
          { oryxType: ORYX.CONFIG.EVENT_DBLCLICK, kisBpmType: KISBPM.eventBus.EVENT_TYPE_DOUBLE_CLICK },
          { oryxType: ORYX.CONFIG.EVENT_MOUSEOUT, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OUT },
          { oryxType: ORYX.CONFIG.EVENT_MOUSEOVER, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OVER }

        ]

        eventMappings.forEach(function (eventMapping) {
          $rootScope.editor.registerOnEvent(eventMapping.oryxType, function (event) {
            KISBPM.eventBus.dispatch(eventMapping.kisBpmType, event)
          })
        })

        $rootScope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, function (event) {
          var validateButton = document.getElementById(event.shape.resourceId + '-validate-button')
          if (validateButton) {
            validateButton.style.display = 'none'
          }
        })

        // The Oryx canvas is ready (we know since we're in this promise callback) and the
        // event bus is ready. The editor is now ready for use
        KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_EDITOR_READY, { type: KISBPM.eventBus.EVENT_TYPE_EDITOR_READY })
      })

      // Alerts
      $rootScope.alerts = {
        queue: []
      }

      $rootScope.showAlert = function (alert) {
        if (alert.queue.length > 0) {
          alert.current = alert.queue.shift()
          // Start timout for message-pruning
          alert.timeout = $timeout(function () {
            if (alert.queue.length == 0) {
              alert.current = undefined
              alert.timeout = undefined
            } else {
              $rootScope.showAlert(alert)
            }
          }, (alert.current.type == 'error' ? 5000 : 1000))
        } else {
          $rootScope.alerts.current = undefined
        }
      }

      $rootScope.addAlert = function (message, type) {
        var newAlert = { message: message, type: type }
        if (!$rootScope.alerts.timeout) {
          // Timeout for message queue is not running, start one
          $rootScope.alerts.queue.push(newAlert)
          $rootScope.showAlert($rootScope.alerts)
        } else {
          $rootScope.alerts.queue.push(newAlert)
        }
      }

      $rootScope.dismissAlert = function () {
        if (!$rootScope.alerts.timeout) {
          $rootScope.alerts.current = undefined
        } else {
          $timeout.cancel($rootScope.alerts.timeout)
          $rootScope.alerts.timeout = undefined
          $rootScope.showAlert($rootScope.alerts)
        }
      }

      $rootScope.addAlertPromise = function (promise, type) {
        if (promise) {
          promise.then(function (data) {
            $rootScope.addAlert(data, type)
          })
        }
      }
    }
  ])

  // Moment-JS date-formatting filter
  .filter('dateformat', function () {
    return function (date, format) {
      if (date) {
        if (format) {
          return moment(date).format(format)
        } else {
          return moment(date).calendar()
        }
      }
      return ''
    }
  })
